////////////////////////////////////////////////////////////////////////////
//
//  Crytek Engine Source File.
//  Copyright (C), Crytek Studios, 2002.
// -------------------------------------------------------------------------
//  File name:   ParticleContainer.h
//  Version:     v1.00
//  Created:     11/03/2010 by Corey (split out from other files).
//  Compilers:   Visual Studio.NET
//  Description: 
// -------------------------------------------------------------------------
//  History:
//
////////////////////////////////////////////////////////////////////////////

#ifndef __particlecontainer_h__
#define __particlecontainer_h__
#pragma once

#include "ParticleEffect.h"
#include "ParticleEnviron.h"
#include "ParticleUtils.h"
#include "ParticleMemory.h"
#include "CREParticle.h"

#undef PlaySound

class CParticle;
class CParticleEmitter;
class CParticleSubEmitter;
class CParticleSource;
struct SParticleUpdateContext;
struct SParticleVertexContext;
struct SContainerCounts;

//////////////////////////////////////////////////////////////////////////
// Particle rendering helper params.
struct SPartRenderParams
{
	CCamera const*				m_pCamera;
	float									m_fCamDistance;
	float									m_fMaxAngularDensity;
	uint16								m_nFogVolumeContribIdx;
	uint32								m_nRenFlags;
	TrinaryFlags<uint32>	m_nRenObjFlags;
	float									m_fHDRDynamicMultiplier;
	float									m_fEmitterScale;
} _ALIGN(16);

//////////////////////////////////////////////////////////////////////////
// Contains, updates, and renders a list of particles, of a single effect

class CParticleContainer : public IParticleVertexCreator, public Cry3DEngineBase
{
public:

	CParticleContainer(CParticleContainer* pParent, CParticleEmitter* pMain, CParticleEffect const* pEffect );
	~CParticleContainer();

	// Associated structures.
	ILINE ResourceParticleParams const& GetParams() const
	{



		return *const_cast<ResourceParticleParams*>(m_pParams); 

	}
	ILINE const CParticleEffect* GetEffect() const						{ return m_pEffect; }
	ILINE CParticleEmitter& GetMain()													{ return *m_pMainEmitter; }
	ILINE const CParticleEmitter& GetMain() const							{ return *m_pMainEmitter; }

	float GetAge() const;

	ILINE CParticleSubEmitter* GetDirectEmitter()				
	{
		if (!m_pParentContainer)
		{
			assert(m_Emitters.size() == 1);
			return &m_Emitters.front();
		}
		return NULL; 
	}

	// If indirect container.
	ILINE CParticleContainer* GetParent() const								{ return m_pParentContainer; }

	void Reset();

	CParticleSubEmitter* AddEmitter( CParticleSource* pSource, CParticleSubEmitter* pParentEmitter );
	CParticle* AddParticle( CParticleSubEmitter* pEmitter );
	void UpdateParticles();
	void EmitParticle( const EmitParticleData* pData );
	uint16 GetNextEmitterSequence()
	{
		return m_nEmitterSequence++;
	}
	void UpdateEffects( const uint32 nEffectFlags );
	float InvalidateStaticBounds();
	void Render( SRendParams const& RenParams, SPartRenderParams const& PRParams );
	void RenderGeometry( const SRendParams& RenParams, const CCamera& cam );
	void RenderDecals();
	void RenderLights();

	// Bounds functions.
	void UpdateState();
	void SetDynamicBounds();

	uint32 NeedsDynamicBounds() const
	{
	#ifndef _RELEASE
		if (GetCVars()->e_ParticlesDebug & AlphaBit('d'))
			// Force all dynamic bounds computation and culling.
			return EFF_DYNAMIC_BOUNDS;
	#endif
		return (m_nEnvFlags | m_nChildFlags) & EFF_DYNAMIC_BOUNDS;
	}
	bool StaticBoundsStable() const
	{
		return GetAge() >= m_fAgeStaticBoundsStable;
	}
	AABB const& GetBounds() const
	{
		return m_bbWorld;
	}
	AABB const& GetStaticBounds() const
	{
		return m_bbWorldStat;
	}
	AABB const& GetDynamicBounds() const
	{
		return m_bbWorldDyn;
	}

	uint32 GetEnvironmentFlags() const;

	bool HasParticles() const
	{
		return m_Particles.capacity() > 0;
	}

	uint32 GetChildFlags() const
	{
		return m_nChildFlags;
	}

	float GetContainerLife() const
	{
		return m_fContainerLife;
	}
	void UpdateContainerLife();

	bool HasLocalTarget() const
	{
		return m_bHasLocalTarget;
	}

	float GetTimeToUpdate() const
	{
		return GetAge() - m_fAgeLastUpdate;
	}

	// Temp functions to update edited effects.
	inline bool IsUsed() const		
	{
		return !m_bUnused;
	}

	void SetUsed(bool b)
	{
		m_bUnused = !b;
	}

	void ClearUnused();

	void AllocParticles();

	void ModifySortBias( float fBias )
	{
		m_fSortBias += fBias;
	}
	int GetHistorySteps() const
	{
		return m_nHistorySteps;
	}
	uint32 NeedsCollisionInfo() const
	{
		return GetEnvironmentFlags() & ENV_COLLIDE_ANY;
	}
	float GetMaxParticleFullLife() const
	{
		return m_fMaxParticleFullLife;
	}

	void OnEffectChange();

	void ComputeUpdateContext( SParticleUpdateContext& context, float fUpdateTime );

	// Stat/profile functions.
	SContainerCounts& GetCounts() 
	{
		return m_Counts; 
	}

	void GetCounts( SParticleCounts& counts ) const;
	
	void GetMemoryUsage( ICrySizer* pSizer ) const;

	//////////////////////////////////////////////////////////////////////////
	// IParticleVertexCreator methods

	VIRTUAL void ComputeVertices( const CCamera& cam, IAllocRender& alloc );
	VIRTUAL float GetDistSquared( const Vec3& vPos ) const;
	VIRTUAL float GetApproxParticleArea () const;

	virtual void AddRef();
	virtual void Release();

	// Other methods.
	bool CanThread();

	void WriteVerticesIndirectImpl( SParticleVertexContext& vcontext, SRenderVertices& alloc );
	void StartComputeVerticesAsJob( CCamera const* pCam, IAllocRender& alloc );
	void WriteVerticesIndirectJob( CCamera const* pCam, Array<SVertexParticle> aVertices, Array<uint16> aVertCounts, float fMaxPixels, CREParticle* pRE );

	void SetNeedSpuUpdate(bool b) 
		{ m_bNeedSpuUpdate = b; }
	bool NeedSpuUpdate() const
		{ return m_bNeedSpuUpdate; }

	void CollectSpuUsage( VecSpuUsageT &vecSpuUsage );
	
	int GetReserveCount() const;
	int GetParticleAvailCount() const
	{
		return m_Particles.capacity() - m_Particles.size();
	}
	int GetEmitterCount() const
	{
		return m_Emitters.size();
	}

	void OffsetPosition(const Vec3 &delta);

private:
	const ResourceParticleParams*		m_pParams;				// Pointer to particle params (effect or code).
	_smart_ptr<CParticleEffect>			m_pEffect;				// Particle effect used for this emitter.
	uint32													m_nEnvFlags;
	uint32													m_nChildFlags;		// Summary of rendering/environment info for child containers.
	float														m_fSortBias;

	ParticlePoolList<CParticleSubEmitter>			m_Emitters;									// All emitters into this container.
	ParticlePoolList<EmitParticleData>				m_DeferredEmitParticles;		// Data for client EmitParticles calls, deferred till next update.
	ParticlePoolList< _smart_ptr<IStatObj> >	m_ExternalStatObjs;					// Any StatObjs from EmitParticle; released on destruction.

	uint16													m_nHistorySteps;
	uint16													m_nEmitterSequence;


	typedef Init1<CParticleContainer*> TParticleInit;
	typedef ParticleCollection<CParticle, ParticleAllocator, true, TParticleInit> 
																	TParticleList;							// Collection with recycled particles.
	TParticleList										m_Particles;

	// Last time when emitter updated, and static bounds validated.
	float														m_fAgeLastUpdate;
	float														m_fAgeStaticBoundsStable;
	float														m_fContainerLife;

	// Final bounding volume for rendering. Also separate static & dynamic volumes for sub-computation.
	AABB														m_bbWorld, m_bbWorldStat, m_bbWorldDyn;

	bool														m_bUnused;									// Temp var used during param editing.
	bool														m_bHasLocalTarget;					// Particles targeted to a parent emitter.
	bool														m_bNeedSpuUpdate;						// Flag to indicate this container needs an update on SPU	
	bool														m_bHasDeferredEmitParticles;	// Deferred particles not emitted yet.

	// Associated structures.
	CParticleContainer*							m_pParentContainer;					// Parent container, if indirect.
	CParticleEmitter*								m_pMainEmitter;							// Emitter owning this container.
	float														m_fMaxParticleFullLife;			// Cached value indicating max update time necessary.

	SContainerCounts								m_Counts;										// Counts for stats.

	// Bounds functions.
	bool NeedsExtendedBounds() const
	{
		// Bounds need extending on movement unless bounds always restricted relative to emitter.
		return !m_pParams->eMoveRelEmitter && !m_pParams->bSpaceLoop;
	}
	void ComputeStaticBounds( AABB& bb, bool bWithSize = true, float fMaxLife = fHUGE );

	float GetEmitterLife() const;
	void UpdateParticleStates( const SParticleUpdateContext& context );

	void EmitParticles( SParticleUpdateContext const& context );

	void SetScreenBounds( SPartRenderParams const &PRParams, const CCamera &cam, uint8 aScreenBounds[4] );
};

#endif // __particlecontainer_h__
